import {isEmpty, isFunction, isNumber, isString, trim} from "lodash-es";
import {newDateIns, changeDateFormat, dateStr2Timestamp,previewPeriod,getTimezone,myFormatTime} from "./index";

export default  class DateUtils {

    static MONTHS_PER_YEAR = 12;
    static DAYS_PER_WEEK = 7;
    static HOURS_PER_DAY = 24;
    static MINUTES_PER_HOUR = 60;
    static SECONDS_PER_MINUTE = 60;

    static MILLIS_PER_SECOND = 1000;
    static MILLIS_PER_MINUTE = DateUtils.SECONDS_PER_MINUTE * DateUtils.MILLIS_PER_SECOND;
    static MILLIS_PER_HOUR = DateUtils.MINUTES_PER_HOUR * DateUtils.MILLIS_PER_MINUTE;
    static MILLIS_PER_DAY = DateUtils.HOURS_PER_DAY * DateUtils.MILLIS_PER_HOUR;


    static NOT_NULL_ASSERTION_MESSAGE = "The dates must not be null.";
    static GMT_TIMEZONE_STRING = "GMT";


    /**
     * 当前系统的 日期格式
     * @type {string}
     */
    static curDateFormat = 'YYYY-MM-DD';

    /**
     * 获取支持输入的日期格式，一般用于设置 DatePicker 组件的 format 属性
     * @returns {string[]}
     */
    getDateFormats = () => (['YYYY-MM-DD', 'YYYY/MM/DD', 'YYYYMMDD', 'YYYY-M-D', 'YYYY/M/D']);

    /**
     * 触发 DatePicker 组件的 Click 事件，以弹出日期选择浮层，并获得输入焦点
     * @param e Event
     * @deprecated
     */
    triggerDateClick = (e: any) => {
        if (e && e.currentTarget) {
            let isFirst = e.currentTarget.getAttribute('isfirst');

            if ('1' !== isFirst) {
                e.currentTarget.setAttribute('isfirst', '1');

                if (e.currentTarget.childNodes && e.currentTarget.childNodes[0] &&
                    e.currentTarget.childNodes[0].childNodes && e.currentTarget.childNodes[0].childNodes[0]) {
                    e.currentTarget.childNodes[0].childNodes[0].click();
                }
            }

        }
    };

    formatCn(cn: string) {
        return window.localUtils && window.localUtils.formatMsgByCn ? window.localUtils.formatMsgByCn(cn) : cn;
    }

    /**
     * 根据开始和结束期间，回显对应的中文
     * @param beginPeriod
     * @param endPeriod
     * @returns {string}
     */
// @ts-ignore
    previewPeriod = (beginPeriod?, endPeriod?) => {
        return previewPeriod(beginPeriod,endPeriod)
    };

    /**
     * new 一个日期实例（dayjs 实例）
     * @param date
     * @param option
     * @param locale
     */
    newDateIns(date?: any, option?: any, locale?: string) {
        return newDateIns(date,option,locale);
    }

    /**
     * 判断 srcDate 是否为 Dayjs 实例
     * @param srcDate any 待判断的参数
     */
    isDateIns(srcDate: any) {
        return srcDate && isFunction(srcDate.format);
    }

    /**
     * 将 srcDate 从 srcFormat 格式转成 distFormat 格式
     * @param srcDate string 当前时区的日期字符串
     * @param srcFormat string srcDate 的日期格式。默认：'YYYY-MM-DD HH:mm:ss'
     * @param distFormat string 日期格式。默认: 'YYYY-MM-DD'
     */
    changeDateFormat(srcDate: string, srcFormat = 'YYYY-MM-DD HH:mm:ss', distFormat = 'YYYY-MM-DD') {
        return changeDateFormat(srcDate,srcFormat,distFormat)
    }

    /**
     * 日期字符串转为时间戳
     * @param srcDate 当前时区的日期字符串或 dayjs 实例。例如：'2020-04-02 12:12:12'
     * @param format 日期格式。例如：'YYYY-MM-DD HH:mm:ss'
     * @return number
     */
    dateStr2Timestamp(srcDate: string | any, format?: string) {
        return dateStr2Timestamp(srcDate,format)
    }

    /**
     * 将 srcDate 转出 format 日期格式
     * @param timestamp string | number | Dayjs 待转换的日期
     * @param format string 日期格式。默认：'YYYY-MM-DD HH:mm:ss'
     */
    myFormatTime(timestamp?: string | number | any, format = '') {
        return  myFormatTime(timestamp, format)
    };

    fixedZero(val: number) {
        return val * 1 < 10 ? `0${val}` : val;
    }

    /**
     * 获取时区；优先级： 缓存的时区 window.localStorage.getItem('TIMEZONE') > 系统时区
     */
    getTimezone() {
        return getTimezone()
    }


    /**
     *  Comparison Functions
     */
    isSameDay(date1:any, date2:any) {

        let result = false;
        if (date1.getFullYear() == date2.getFullYear() && date1.getMonth() == date2.getMonth() && date1.getDate() == date2.getDate())
            result = true;

        return result;
    }


    /**
     * 获取两个日期的周数差
     *
     * @param startTime
     * @param endTime
     * @return
     */
    getWeekDiff(startTime:any, endTime:any) {
        let dayDiff = this.getDaysDiff(startTime, endTime);
        let result = dayDiff / 7;
        let sm = (startTime.day + 1) % 7;
        if (sm > 1)
            result++;
        let add = this.addWeeks(this.getStartOfWeek(startTime), result);
        if (endTime.getTime() > add.getTime())
            result++;
        return result;
    }

    /**
     * 获取指定日期季度开始时间
     *
     * @param date
     * @return
     */
    getStartOfQuarter(date:any) {
        let tmp  :any = new Date();
        tmp.time = date.getTime();
        let m = tmp.getMonth() + 1;
        if (m >= 1 && m <= 3)
            tmp.month = 0;
        else if (m >= 4 && m <= 6)
            tmp.month = 3;
        else if (m >= 7 && m <= 9)
            tmp.month = 6;
        else
            tmp.month = 9;
        return this.getStartOfMonth(tmp);
    }

    /**
     * 获取指定日期季度结束
     * @param date
     * @return
     *
     */
    getEndOfQuarter(date:any) {

        let tmp :any= new Date();
        tmp.time = date.getTime();
        let m = tmp.getMonth() + 1;

        if (m >= 1 && m <= 3)
            tmp.month = 2;
        else if (m >= 4 && m <= 6)
            tmp.month = 5;
        else if (m >= 7 && m <= 9)
            tmp.month = 8;
        else
            tmp.month = 11;
        return this.getEndOfMonth(tmp);
    }

    /**
     * 获取两个时间季度想差数
     */
    getQuarterDiff(startTime:any, endTime:any) {

        let sd = this.getStartOfQuarter(startTime);
        let ed = this.getEndOfQuarter(endTime);

        let mdiff = this.getMonthDiff(sd, ed);
        let result = mdiff / 3;
        return result == 0 ? 1 : result;
    }

    /**
     * 获取指定日期半年开始时间
     *
     * @param date
     * @return
     */
    getStartOfHalfYear(date:any) {

        let tmp :any= new Date();
        tmp.time = date.getTime();
        let m = tmp.getMonth() + 1;
        if (m >= 1 && m <= 6)
            tmp.month = 0;
        else
            tmp.month = 6;
        return this.getStartOfMonth(tmp);
    }

    /**
     * 获取指定日期半年结束时间
     * @param date
     * @return
     *
     */
    getEndOfHalfYear(date:any) {

        let tmp :any= new Date();
        tmp.time = date.getTime();
        let m = tmp.getMonth() + 1;
        if (m >= 1 && m <= 6)
            tmp.month = 5;
        else
            tmp.month = 11;
        return this.getEndOfMonth(tmp);
    }


    /**
     * 获取两个时间半年相差数
     */
    getHalfYearDiff(startTime:any, endTime:any) {

        let sd = this.getStartOfHalfYear(startTime);
        let ed = this.getEndOfHalfYear(endTime);

        let mdiff = this.getMonthDiff(sd, ed);
        let result = mdiff / 6;
        return result == 0 ? 1 : result;
    }

    /**
     * 获取两个日期的月数差
     * @param startTime
     * @param endTime
     * @return
     */
    getMonthDiff(startTime:any, endTime:any) {

        let startYear = startTime.getFullYear();
        let endYear = endTime.getFullYear();
        if (startYear == endYear) {
            return Math.abs(startTime.getMonth() - endTime.getMonth()) + 1;
        } else {
            let fromMonth = 12 - (startTime.getMonth() + 1);
            let toMonth = endTime.getMonth() + 1;
            return Math.abs(endYear - startYear - 1) * 12 + fromMonth + toMonth + 1;
        }
    }


    /**
     * 获取日期一年中的第几周1-52
     *
     * @param date
     * @return
     */
    getWeek(date:any) {
        let days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        let year = date.getFullYear();
        let isLeap = ((year % 4 == 0) && (year % 100 != 0)) || ((year % 100 == 0) && (year % 400 == 0));
        if (isLeap)
            days[1]++;

        let d :any = 0;
        //month is conveniently 0 indexed.
        for (let i = 0; i < date.getMonth(); i++)
            d += days[i];
        d += date.getMonth();

        let temp:any = new Date(year, 0, 1);
        let jan1 :any = temp.day;
        d += jan1;
        let result = parseInt(String(d / 7)) + 1;
        if (result == 53)
            result = 1;
        return result;
    }

    isSameInstant(date1:any, date2:any) {
        let result = false;
        if (date1.getTime() == date2.getTime())
            result = true;

        return result;
    }


    /**
     * Add Function
     *
     * */
    addYears(date:any, years:number) {
        return this.addMonths(date, years * DateUtils.MONTHS_PER_YEAR);
    }

    addMonths(date:any, months:number) {
        if (!date) return;
        let result = new Date(date.getTime());
        result.setMonth(date.getMonth() + months);
        result = this.handleShorterMonth(date, result);
        return result;

    }

    addWeeks(date:any, weeks:number) {
        return this.addDays(date, weeks * DateUtils.DAYS_PER_WEEK);
    }

    addDays(date:any, days:number) {
        let result = this.add(date, DateUtils.MILLIS_PER_DAY, days);
        return this.handleDaylightSavingsTime(date, result);
    }

    addHours(date:any, hours:number) {
        return this.add(date, DateUtils.MILLIS_PER_HOUR, hours);
    }

    addMinutes(date:any, minutes:number) {
        return this.add(date, DateUtils.MILLIS_PER_MINUTE, minutes);
    }

    addSeconds(date:any, seconds:number) {
        return this.add(date, DateUtils.MILLIS_PER_SECOND, seconds);
    }

    addMilliseconds(date:any, milliseconds:number) {
        return this.add(date, 1, milliseconds);
    }

    add(date:any, multiplier:number, num:number) {
        let resultTime = date.getTime() + multiplier * num;
        return new Date(resultTime);
    }

    /***
     * Set Function
     */
    setYear(date:any, year:number) {
        let result = new Date(year, date.getMonth(), date.getDate(), date.getHours(),
            date.getMinutes(), date.getSeconds(), date.getMilliseconds());
        return this.handleShorterMonth(date, result);
    }

    setMonth(date:any, month:number) {
        let result = new Date(date.getFullYear(), month, date.getDate(),
            date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
        return this.handleShorterMonth(date, result);
    }

    setDay(date:any, day:number) {
        return new Date(date.getFullYear(), date.getMonth(), day,
            date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
    }

    setHours(date:any, hour:number) {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate(),
            hour, date.getMinutes(), date.getSeconds(), date.getMilliseconds());
    }

    setMinutes(date:any, minute:number) {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate(),
            date.getHours(), minute, date.getSeconds(), date.getMilliseconds());
    }

    setSeconds(date:any, second:number) {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate(),
            date.getHours(), date.getMinutes(), second, date.getMilliseconds());
    }

    setMilliseconds(date:any, millisecond:number) {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate(),
            date.getHours(), date.getMinutes(), date.getSeconds(), millisecond);
    }

    /*
          * Conversion Functions
          */
    getUTCDate(date:any) {
        return this.getDateForOffset(date, date.getTimezoneOffset());
    }

    getDateForOffset(date:any, offsetMinutes:number) {
        let offsetMilliseconds = offsetMinutes * DateUtils.MILLIS_PER_MINUTE;
        return new Date(date.getTime() + offsetMilliseconds);
    }

    /*
          * Period Functions
          */
    getStartOfYear(date:any) {
        return this.getStartOfDay(new Date(date.getFullYear(), 0, 1));
    }

    getStartOfMonth(date:any) {
        return this.getStartOfDay(new Date(date.getFullYear(), date.month, 1));
    }

    getStartOfWeek(date:any) {
        return this.getStartOfDay(new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay()));
    }

    getStartOfDay(date:any) {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
    }

    getUTCStartOfDay(date:any) {
        return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0, 0));
    }

    getEndOfYear(date:any) {
        return this.getEndOfDay(new Date(date.getFullYear(), 11, 31));
    }

    getEndOfMonth(date:any) {
        let lastDateOfMonth :any= this.getStartOfMonth(date);
        lastDateOfMonth = this.addMonths(lastDateOfMonth, 1);
        lastDateOfMonth = this.addDays(lastDateOfMonth, -1);
        return this.getEndOfDay(new Date(lastDateOfMonth.getFullYear(), lastDateOfMonth.getMonth(), lastDateOfMonth.getDate()));
    }

    getEndOfWeek(date:any) {
        return this.getEndOfDay(new Date(date.getFullYear(), date.getMonth(), date.getDate() - date.getDay() + 6));
    }

    getEndOfDay(date:any) {
        return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
    }

    getUTCEndOfDay(date:any) {
        return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 23, 59, 59, 999));
    }

    /**
     * Diff functions
     */
    getDaysDiff(startDate:any, endDate:any) {
        return this.getletiableDiff(startDate, endDate, DateUtils.MILLIS_PER_DAY);
    }

    getHoursDiff(startDate:any, endDate:any) {
        return this.getletiableDiff(startDate, endDate, DateUtils.MILLIS_PER_HOUR);
    }

    getMinutesDiff(startDate:any, endDate:any) {
        return this.getletiableDiff(startDate, endDate, DateUtils.MILLIS_PER_MINUTE);
    }

    getletiableDiff(startDate:any, endDate:any, millsPer:number) {
        let startMillis = startDate.getTime();
        let endMillis = endDate.getTime();
        let dstOffset = (startDate.getTimezoneOffset() - endDate.getTimezoneOffset()) * DateUtils.MILLIS_PER_MINUTE;
        return Math.ceil((endMillis - startMillis + dstOffset) / millsPer);
    }

    /**
     * Misc Functions
     */

    getLocalTimeZoneCode() {
        let dateString = new Date().toString();
        let startIndex = dateString.indexOf(DateUtils.GMT_TIMEZONE_STRING);
        let endIndex = dateString.indexOf(" ", startIndex);

        return dateString.substring(startIndex, endIndex);
    }

    isLeapYear(date:any) {
        let year = date.getFullYear();
        return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
    }

    isWeekDay(date:any) {
        return !(this.isWeekEnd(date));
    }

    isWeekEnd(date:any) {
        let dayOfWeek = date.getDay();
        return dayOfWeek == 0 || dayOfWeek == 6;
    }

    getYesterday() {
        let result = new Date();
        result.setDate(result.getDate() - 1);
        return result;
    }

    getTomorrow() {
        let result = new Date();
        result.setDate(result.getDate() + 1);
        return result;
    }


    /**
     * helper methods
     *
     * */
    handleShorterMonth(originalDate:any, newDate:any) {
        let result = newDate;
        let originalDayOfMonth = originalDate.getDate();
        if (originalDayOfMonth > result.getDate()) {
            result = this.addDays(newDate, -(newDate.getDate()));
        }
        ;
        return result;
    }

    handleDaylightSavingsTime(originalDate:any, newDate:any) {
        let result = newDate;
        let originalHours = originalDate.getHours();
        if (originalHours != result.getHours())
            result = this.addHours(result, -(result.getHours() - originalHours));
        let originalMinutes = originalDate.getMinutes();
        if (originalMinutes != result.getMinutes())
            result = this.addMinutes(result, -(result.getMinutes() - originalMinutes));
        let originalOffset = originalDate.getTimezoneOffset();
        if (originalOffset != result.getTimezoneOffset())
            result = this.addMinutes(result, -(result.getTimezoneOffset() - originalOffset));

        return result;
    }


}
